"""
该类为节点生成与节点数据处理类

几个关键节点：
一、数据Data_IO类节点，处理数据的导入导出节点
    1.文件类数据
    读取Excel,CSV
    2.外部传入的数字数据。

建议数据处理还是根据库的计算类型来实现，中途可以实现一些自动转化过程来降低对程序的认知。
还有一个关键点就是 数据转化节点，比如将字典转成json,将多个点的各属性值转成DataFrame等等


先从pandas节点开始，这是对外部数据的处理，包括现在正在做的任务分类。



DataFrame节点：创建一个pandas.DataFrame
Numpy节点: 将数据转成Numpy, 所以numpy计算类节点是不带np的标识

TF节点：tensorflow节点，
SK节点：Sklean节点，进行数据拆分



"""
from functools import wraps
from .item import *



def node_spawn(func):
    """
    节点构建器
    :param func:
    :return:
    """
    # @wraps(func)
    # def spawn(*args,**kwargs):
    #     ori_func = func(*args,**kwargs)   #args[0]:nodegraph,args[1]:nodename
    #     print('node_spawn:',args)  # args[0]:cgai.node.Node 类型, args[1]:node_name节点名, args[2]:cgai.graph.NodeGraph 所在的节点图
    #     b_item = ALL_ITEMS[args[1]](args[2])  #生成BodyItem
    #     input_pcounts = b_item.get_input_pcounts()
    #     output_pcounts = b_item.get_output_pcounts()
    #     up_pcounts = input_pcounts - 1  #顶端输入端口数
    #     down_pcounts = output_pcounts - 1  #底端输入端口数
    #
    #
    #     width = b_item.width
    #     height = b_item.height
    #
    #     left_input_pitem = IPointItem(CGAI.LEFT,parent=b_item)
    #     up_input_pitem = IPointItem(CGAI.UP,parent=b_item)
    #     right_output_pitem = OPointItem(CGAI.RIGHT,parent=b_item)
    #     down_output_pitem = OPointItem(CGAI.DOWN,parent=b_item)
    #     status_item = StatusItem(parent=b_item)
    #
    #
    #     radius = left_input_pitem.radius
    #     #绘制线时的偏移量，不需要算半径才能保证线连接正确
    #     left_offset_pos=QPointF(-radius/2,height/2)
    #     right_offset_pos = QPointF(width + radius / 2, height / 2)
    #
    #     # up_offset_pos=QPointF(width/2,-radius/2)     # 上连点偏移  绘制线时的偏移量，不需要算半径才能保证线连接正确
    #     up_offset_pos=QPointF(width/2,-radius/2)     # 上连点偏移  绘制线时的偏移量，不需要算半径才能保证线连接正确
    #
    #
    #     # down_offset_pos=QPointF(width/2,height+radius/2)   # 下连点偏移    #绘制线的偏移量，不需要算半径
    #
    #     down_offset_pos=QPointF(width/2,height+radius/2)   # 下连点偏移    #绘制线的偏移量，不需要算半径
    #     status_offset_pos = QPointF(width-radius/2,height+radius/2)
    #
    #     #设置连点状态点的UI位置
    #     left_input_pitem.setPos(-radius,height/2-radius/2)
    #     left_input_pitem.set_offset_pos(left_offset_pos)  #item存入偏移量
    #
    #     up_input_pitem.setPos(width/2-radius/2,-radius)
    #     up_input_pitem.set_offset_pos(up_offset_pos)  #item存入偏移量
    #
    #     right_output_pitem.setPos(width,height/2-radius/2)
    #     right_output_pitem.set_offset_pos(right_offset_pos)  #item存入偏移量
    #
    #     down_output_pitem.setPos(width/2-radius/2,height)
    #     down_output_pitem.set_offset_pos(down_offset_pos)    #item存入偏移量
    #
    #     status_item.setPos(width-radius*2,height+radius/3)
    #
    #     b_item.set_input_pitems(left_input_pitem,up_input_pitem)
    #     b_item.set_output_pitems(right_output_pitem,down_output_pitem)
    #     b_item.set_status_item(status_item)
    #
    #     return b_item
    # return spawn

    @wraps(func)
    def spawn(*args, **kwargs):
        ori_func = func(*args, **kwargs)  # args[0]:nodegraph,args[1]:nodename
        print('node_spawn:',args)  # args[0]:cgai.node.Node 类型, args[1]:node_type节点类型名称, args[2]:cgai.graph.NodeGraph 所在的节点图
        node_type = args[1]
        graph = args[2]
        node_name = kwargs.get('node_name','')
        node_number = graph._get_right_number(node_type)
        node_name = node_name if node_name else node_type+str(node_number)

        # b_item = ALL_ITEMS[args[1]](args[2],kwargs.get('node_name',''))  # 生成BodyItem   kwargs['node_name']为传入的节点名称
        b_item = ALL_ITEMS[node_type](graph,node_name)  # 生成BodyItem   kwargs['node_name']为传入的节点名称

        input_pcounts = b_item.get_input_pcounts()
        output_pcounts = b_item.get_output_pcounts()
        up_pcounts = input_pcounts - 1  # 顶端输入端口数
        down_pcounts = output_pcounts - 1  # 底端输入端口数

        width = b_item.width
        height = b_item.height

        input_pitems = {}  #输入点  {0:IPointItem,1:IPointItem,...}  以端口序号为key
        output_pitems = {}  #输入点  {0:OPointItem,1:OPointItem,...}

        left_input_pitem = IPointItem(CGAI.LEFT, parent=b_item)     #左端输入点
        right_output_pitem = OPointItem(CGAI.RIGHT, parent=b_item)  #右端输出点
        radius = left_input_pitem.radius   #计算连点半径
        left_offset_pos = QPointF(-radius / 2, height / 2)  #计算左连点位置
        right_offset_pos = QPointF(width + radius / 2, height / 2)   #计算右连点位置
        # 设置连点状态点的UI位置
        left_input_pitem.setPos(-radius, height / 2 - radius / 2)
        left_input_pitem.set_offset_pos(left_offset_pos)  # item存入偏移量
        right_output_pitem.setPos(width, height / 2 - radius / 2)
        right_output_pitem.set_offset_pos(right_offset_pos)  # item存入偏移量

        status_item = StatusItem(parent=b_item) #状态节点
        # status_offset_pos = QPointF(width - radius / 2, height + radius / 2)
        status_item.setPos(width - radius * 2, height + radius / 3)

        input_pitems[0] = left_input_pitem
        output_pitems[0] = right_output_pitem

        # 生成顶部输入连点

        for up in range(up_pcounts):
            up_input_pitem = IPointItem(CGAI.UP, parent=b_item)
            up_offset_pos = QPointF((width / input_pcounts)*(up+1)  , -radius / 2) # 上连点偏移  绘制线时的偏移量，不需要算半径才能保证线连接正确
            up_input_pitem.setPos((width / input_pcounts)*(up+1) - radius / 2 , -radius)
            up_input_pitem.set_offset_pos(up_offset_pos)  # item存入偏移量
            input_pitems[up+1] = up_input_pitem

        # 生成底部输出连点
        for down in range(down_pcounts):
            down_output_pitem = OPointItem(CGAI.DOWN, parent=b_item)
            down_offset_pos = QPointF((width / output_pcounts)*(down+1), height + radius / 2)  # 下连点偏移    #绘制线的偏移量，不需要算半径
            down_output_pitem.setPos((width / output_pcounts)*(down+1) - radius / 2, height)
            down_output_pitem.set_offset_pos(down_offset_pos)  # item存入偏移量
            output_pitems[down+1] = down_output_pitem




        # b_item.set_input_pitems(left_input_pitem, up_input_pitem)
        b_item.set_input_pitems(input_pitems)
        # b_item.set_output_pitems(right_output_pitem, down_output_pitem)
        b_item.set_output_pitems(output_pitems)
        b_item.set_status_item(status_item)

        # graph._set_right_number(node_type)  #更新该类节点序号
        return b_item

    return spawn


class Node(object):

    def __init__(self,nodegraph):
        super(Node, self).__init__()
        self.nodegraph=nodegraph

    def register_nodes(self,data):
        """
        注册自定义节点，传入的是多个节点构成信息。每个节点的基本架构如下.
        需要的内容：节点名称:node_name
                   节点显示标签：node_label
                   节点图标：node_icon 传入节点显示图标路径
                   节点参数与值: parms 标识键
                                值为一个字典，键parm_name为每个自定义参数名称parm_name
                                             值parm_data为一个字典，包括:
                                                    parm_index:参数显示的索引位置，按顺序显示在UI界面上。注:参数面板都是从上到下垂直添加组件
                                                    parm_label:参数的显示标签名称
                                                    parm_type: 参数类型，该类型决定界面的参数的显示方式，通过parm类来标识
                                                    value: 可以为str,int,float,list,dict,tuple类型,注意要与声明的parm_type对应
        单个节点结构如下：
        node_data:{'node_name':xxx,'node_label':xxx,'node_icon':xxx,'parms':{'parm_name':parm_data,'parm_name':parm_data,...}}

        :param data: {node_data,node_data,...}
        :return:
        """
        pass

    @node_spawn
    def createNode(self,node_type,graph,node_name=None,data=None):
        graph._set_right_number(node_type)  # 更新该类节点序号
        return None


    # def createDefaultNode(self,nodegrap):
    #
    #     b_item = CGAIItem(nodegrap)
    #     width=b_item.width
    #     height=b_item.height
    #
    #
    #     left_input_pitem = IPointItem(CGAI.LEFT,parent=b_item)
    #     up_input_pitem = IPointItem(CGAI.UP,parent=b_item)
    #     right_output_pitem=OPointItem(CGAI.RIGHT,parent=b_item)
    #     down_output_pitem=OPointItem(CGAI.DOWN,parent=b_item)
    #     status_item=StatusItem(parent=b_item)
    #
    #
    #     radius = left_input_pitem.radius
    #     #绘制线时的偏移量，不需要算半径才能保证线连接正确
    #     left_offset_pos=QPointF(-radius/2,height/2)
    #     up_offset_pos=QPointF(width/2,-radius/2)     # 上连点偏移  绘制线时的偏移量，不需要算半径才能保证线连接正确
    #     right_offset_pos = QPointF(width+radius/2,height/2)
    #     down_offset_pos=QPointF(width/2,height+radius/2)   # 下连点偏移    #绘制线的偏移量，不需要算半径
    #     status_offset_pos = QPointF(width-radius/2,height+radius/2)
    #
    #     #设置连点状态点的UI位置
    #     left_input_pitem.setPos(-radius,height/2-radius/2)
    #     left_input_pitem.set_offset_pos(left_offset_pos)  #item存入偏移量
    #
    #     up_input_pitem.setPos(width/2-radius/2,-radius)
    #     up_input_pitem.set_offset_pos(up_offset_pos)  #item存入偏移量
    #
    #     right_output_pitem.setPos(width,height/2-radius/2)
    #     right_output_pitem.set_offset_pos(right_offset_pos)  #item存入偏移量
    #
    #     down_output_pitem.setPos(width/2-radius/2,height)
    #     down_output_pitem.set_offset_pos(down_offset_pos)    #item存入偏移量
    #
    #     status_item.setPos(width-radius*2,height+radius/3)
    #
    #     b_item.set_input_pitems(left_input_pitem,up_input_pitem)
    #     b_item.set_output_pitems(right_output_pitem,down_output_pitem)
    #     b_item.set_status_item(status_item)
    #
    #
    #
    #
    #     return b_item




